package com.bird.generate.ddd.domain.generateutil

import cn.hutool.core.util.StrUtil
import com.bird.component.lang.util.nonnull
import com.bird.generate.ddd.domain.AggregateRootEntity
import com.bird.generate.ddd.domain.JAVA_PATH
import com.bird.generate.ddd.domain.appendNextPath
import com.bird.generate.ddd.domain.createFile
import com.bird.generate.ddd.domain.generatePackageInfo
import com.bird.generate.ddd.domain.vo.Field
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter

class AdapterGenerate {

    companion object {

        /**
         * 生成adapter代码
         */
        fun adapterGenerate(path: String,aggregateRoot: AggregateRootEntity) {
            var adapterRootPath = path.appendNextPath(aggregateRoot.moduleName.lowercase()+"bc").appendNextPath("${aggregateRoot.moduleName.lowercase()}bc-southbound").appendNextPath("${aggregateRoot.moduleName.lowercase()}bc-southbound-adapter").appendNextPath(JAVA_PATH)
                .appendNextPath("${aggregateRoot.comPrefix.replace(".","/")}/${aggregateRoot.projectName.lowercase()}prd/${aggregateRoot.moduleName.lowercase()}bc/southbound/adapter/${aggregateRoot.enName.lowercase()}aggr")
            //root 包路径
            var adapterJavaPath = "${aggregateRoot.comPrefix.replace(".","/")}/${aggregateRoot.projectName.lowercase()}prd/${aggregateRoot.moduleName.lowercase()}bc/southbound/adapter/${aggregateRoot.enName.lowercase()}aggr".replace("/", ".")

            aggregateRoot.valueObjects.forEach { vo ->
                if (vo.fields.isEmpty()) {
                    vo.fields = listOf(
                        Field(
                            enName = vo.enName,
                            name = vo.name,
                            type = vo.type,
                        )
                    )
                }else if (vo.fields.size == 1 && vo.fields[0].enName == "value") {
                    vo.fields = listOf(
                        Field(
                            enName = vo.enName,
                            name = vo.name,
                            type = "String",
                        )
                    )
                }
            }

            //创建两个pom.xml
            generateSouthboundPomXml(aggregateRoot,path.appendNextPath(aggregateRoot.moduleName.lowercase()+"bc").appendNextPath("${aggregateRoot.moduleName.lowercase()}bc-southbound"))
            generateAdapterPomXml(aggregateRoot,path.appendNextPath(aggregateRoot.moduleName.lowercase()+"bc").appendNextPath("${aggregateRoot.moduleName.lowercase()}bc-southbound").appendNextPath("${aggregateRoot.moduleName.lowercase()}bc-southbound-adapter"))
            //生成DO
            generateDO(aggregateRoot, adapterRootPath, adapterJavaPath)
            //生成package-info.java
            generatePackageInfo(adapterRootPath.substringBeforeLast("/"), adapterJavaPath.substringBeforeLast("."))
            //生成Converter
            generateConverter(aggregateRoot, adapterRootPath, adapterJavaPath)
            //生成RepositoryAdapter
            generateRepositoryAdapter(aggregateRoot, adapterRootPath, adapterJavaPath)
            //资源网关adapter
            generateResourceGatewayAdapter(aggregateRoot, adapterRootPath, adapterJavaPath)
        }

        private fun generateResourceGatewayAdapter(
            aggregateRoot: AggregateRootEntity,
            adapterRootPath: String,
            adapterJavaPath: String
        ) {
            //southbound.adapter变domain
            val domainJavaPath = adapterJavaPath.replace("southbound.adapter", "domain")
            val resourceGatewayContent = """
package ${adapterJavaPath};

import ${domainJavaPath}.port.${StrUtil.upperFirst(aggregateRoot.enName)}DomainResourceGateway;

 /**
  * ${aggregateRoot.name}资源网关适配器
  * @author ${aggregateRoot.creator}<p>
  * ================================<p>
  * Date: ${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))}<p>
  * Time: ${LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"))}<p>
  * ================================
  */
public class ${StrUtil.upperFirst(aggregateRoot.enName)}DomainResourceGatewayAdapter implements ${StrUtil.upperFirst(aggregateRoot.enName)}DomainResourceGateway {
   
}
                """
            createFile(adapterRootPath.appendNextPath("${StrUtil.upperFirst(aggregateRoot.enName)}DomainResourceGatewayAdapter.java"), resourceGatewayContent)
        }

        private fun generateRepositoryAdapter(
            aggregateRoot: AggregateRootEntity,
            adapterRootPath: String,
            adapterJavaPath: String
        ) {
            //southbound.adapter变domain
            val domainJavaPath = adapterJavaPath.replace("southbound.adapter", "domain")
            val repositoryAdapterContent = """
package $adapterJavaPath;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import $domainJavaPath.*;
import $domainJavaPath.port.*;
import $domainJavaPath.valueobject.*;
import com.zz.core.tool.utils.Func;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Optional;

/**
  * ${aggregateRoot.name} 仓储接口(命令)适配器
  * @author ${aggregateRoot.creator}<p>
  * ================================<p>
  * Date: ${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))}<p>
  * Time: ${LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"))}<p>
  * ================================
  */
@Component
@RequiredArgsConstructor
public class ${StrUtil.upperFirst(aggregateRoot.enName)}CommandRepositoryAdapter implements ${
                StrUtil.upperFirst(
                    aggregateRoot.enName
                )
            }CommandRepository {
    
    //${aggregateRoot.name} mybatis Mapper
    private final ${StrUtil.upperFirst(aggregateRoot.enName)}Mapper ${StrUtil.lowerFirst(aggregateRoot.enName)}Mapper;
    ${
                aggregateRoot.entities.map {
                    """
    //${it.name} mybatis Mapper
    private final ${StrUtil.upperFirst(it.enName)}Mapper ${StrUtil.lowerFirst(it.enName)}Mapper;
                    """
                }.joinToString("\n")
            }

    /**
     * 保存${aggregateRoot.name}
     * @param ${StrUtil.lowerFirst(aggregateRoot.enName)}Entity ${aggregateRoot.name}聚合根实体
     */
    @Override
    public void save${StrUtil.upperFirst(aggregateRoot.enName)}(${StrUtil.upperFirst(aggregateRoot.enName)}AggregateRootEntity ${StrUtil.lowerFirst(aggregateRoot.enName)}Entity) {
        //保存${aggregateRoot.name}聚合根实体
        doSave${StrUtil.upperFirst(aggregateRoot.enName)}(${StrUtil.lowerFirst(aggregateRoot.enName)}Entity);
        ${
                aggregateRoot.entities.map {
                    """
        //todo 保存${it.name}实体
        doSave${StrUtil.upperFirst(it.enName)}(${StrUtil.lowerFirst(aggregateRoot.enName)}Entity, ${StrUtil.lowerFirst(it.enName)}Entity);
                        """
                }.joinToString("\n")
        }
    }
    
    ${
                aggregateRoot.entities.map {
                    """
    /**
     * 保存${it.name}
     * @param ${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity ${aggregateRoot.name}聚合根实体
     * @param ${StrUtil.lowerFirst(it.enName)} ${it.name}实体
     */
    private void doSave${StrUtil.upperFirst(it.enName)}(${StrUtil.upperFirst(aggregateRoot.enName)}AggregateRootEntity ${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity,
                                               ${StrUtil.upperFirst(it.enName)}Entity ${StrUtil.lowerFirst(it.enName)}) {
        if (Func.isEmpty(${StrUtil.lowerFirst(it.enName)})) {
            return;
        }
        //保存${it.name}
        ${StrUtil.upperFirst(it.enName)}DO ${StrUtil.lowerFirst(it.enName)}DO = ${StrUtil.upperFirst(aggregateRoot.enName)}Converter.INSTANCE.${StrUtil.lowerFirst(it.enName)}EntityToDO(${StrUtil.lowerFirst(it.enName)});
        // todo 设置与${aggregateRoot.name}关联
        
        switch (${StrUtil.lowerFirst(it.enName)}.getChangingStatus()) {
            case NEW:
                ${StrUtil.lowerFirst(it.enName)}Mapper.insert(${StrUtil.lowerFirst(it.enName)}DO);
                ${StrUtil.lowerFirst(it.enName)}.setId(new ${StrUtil.upperFirst(it.enName)}Id(${StrUtil.lowerFirst(it.enName)}DO.getId()));
                ${StrUtil.lowerFirst(it.enName)}.toUnChang();
                break;
            case UPDATED:
                ${StrUtil.lowerFirst(it.enName)}Mapper.updateById(${StrUtil.lowerFirst(it.enName)}DO);
                ${StrUtil.lowerFirst(it.enName)}.toUnChang();
                break;
            case DELETED:
                ${StrUtil.lowerFirst(it.enName)}Mapper.deleteById(${StrUtil.lowerFirst(it.enName)}DO);
                break;
            default:
                break;
        }

    }
                        """
                }.joinToString("\n\n")
        
    }
 
    /**
     * 保存${aggregateRoot.name}
     * @param ${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity ${aggregateRoot.name}聚合根实体
     */
    private void doSave${StrUtil.upperFirst(aggregateRoot.enName)}(${StrUtil.upperFirst(aggregateRoot.enName)}AggregateRootEntity ${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity) {
        ${StrUtil.upperFirst(aggregateRoot.enName)}DO ${StrUtil.lowerFirst(aggregateRoot.enName)}DO = ${
                StrUtil.upperFirst(
                    aggregateRoot.enName
                )
            }Converter.INSTANCE.${StrUtil.lowerFirst(aggregateRoot.enName)}EntityToDO(${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity);
        switch (${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity.getChangingStatus()) {
            case NEW:
                ${StrUtil.lowerFirst(aggregateRoot.enName)}Mapper.insert(${StrUtil.lowerFirst(aggregateRoot.enName)}DO);
                ${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity.setId(new ${
                StrUtil.upperFirst(
                    aggregateRoot.enName
                )
            }Id(${StrUtil.lowerFirst(aggregateRoot.enName)}DO.getId()));
                ${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity.toUnChang();
                break;
            case UPDATED:
                ${StrUtil.lowerFirst(aggregateRoot.enName)}Mapper.updateById(${StrUtil.lowerFirst(aggregateRoot.enName)}DO);
                ${StrUtil.lowerFirst(aggregateRoot.enName)}AggregateRootEntity.toUnChang();
                break;
            case DELETED:
                ${StrUtil.lowerFirst(aggregateRoot.enName)}Mapper.deleteById(${StrUtil.lowerFirst(aggregateRoot.enName)}DO);
                break;
            default:
                break;
        }
    }
    
    private LambdaQueryWrapper<${StrUtil.upperFirst(aggregateRoot.enName)}DO> ${StrUtil.lowerFirst(aggregateRoot.enName)}Wrapper() {
        return new LambdaQueryWrapper<>();
    }
    ${
                aggregateRoot.entities.map {
                    """
    private LambdaQueryWrapper<${StrUtil.upperFirst(it.enName)}DO> ${StrUtil.lowerFirst(it.enName)}Wrapper() {
        return new LambdaQueryWrapper<>();
    }
                        """
                }.joinToString("\n")
    }
}

               """
            createFile(adapterRootPath.appendNextPath("${StrUtil.upperFirst(aggregateRoot.enName)}CommandRepositoryAdapter.java"), repositoryAdapterContent)

        }

        /**
         * 生成DO
         */
        private fun generateDO(rootEntity: AggregateRootEntity, adapterRootPath: String, adapterJavaPath: String) {
            var valueObjects = rootEntity.valueObjects
            valueObjects.forEach {
                if (it.isEnum) {
                    it.fields = listOf(Field(
                        enName = it.enName,
                        name = it.name,
                        type = "String",
                    ))
                }
            }
            //取出files
            var rootFields = rootEntity.fields
            val map = rootFields.map { f -> f.enName }
            //取出所有实体包含的值对象
            var rootExistFields = valueObjects.filter { map.contains(it.enName) }.map { it.fields }.flatten()
            rootExistFields = rootExistFields + rootEntity.fields.filter { it.type?.isNotBlank() == true }

            //生成DO
            generateDO(
                rootEntity,
                rootEntity.name,
                rootEntity.enName,
                adapterRootPath,
                adapterJavaPath,
                rootExistFields
            )
            val entities = rootEntity.entities
            entities.forEach {
                var entityFields = it.fields
                var entityExistFields = valueObjects.filter { entityFields.map { f -> f.enName }.contains(it.enName) }.map { it.fields }.flatten()
                entityExistFields = entityExistFields + entityFields.filter { it.type.nonnull() }
                generateDO(
                    rootEntity,
                    it.name,
                    it.enName,
                    adapterRootPath,
                    adapterJavaPath,
                    entityExistFields
                )
            }


        }

        private fun generateConverter(
            rootEntity: AggregateRootEntity,
            adapterRootPath: String,
            adapterJavaPath: String
        ) {
            //southbound.adapter变domain
            val domainJavaPath = adapterJavaPath.replace("southbound.adapter", "domain")
            val valueObjects = rootEntity.valueObjects
            var rootValueObjects = valueObjects.filter { rootEntity.fields.map { f -> f.enName }.contains(it.enName) }.filter { it.fields.size > 1 }



            val converterContent = """
package $adapterJavaPath;

import $domainJavaPath.*;
import $domainJavaPath.valueobject.*;
import com.zz.core.ddd.common.mapstruct.CommonMapping;
import com.zz.core.ddd.common.mapstruct.TenantMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;

@Mapper(uses = {TenantMapping.class, ${StrUtil.upperFirst(rootEntity.enName)}Mapping.class, CommonMapping.class})
public interface ${StrUtil.upperFirst(rootEntity.enName)}Converter {
    ${StrUtil.upperFirst(rootEntity.enName)}Converter INSTANCE = Mappers.getMapper(${StrUtil.upperFirst(rootEntity.enName)}Converter.class);

    /**
     * 实体转DO ${rootEntity.name}
     */${rootValueObjects.map{vo -> vo.fields.map { field -> """
    @Mapping(target = "${StrUtil.lowerFirst(field.enName)}", source = "${StrUtil.lowerFirst(vo.enName)}", qualifiedByName = "to${StrUtil.upperFirst(field.enName)}")"""
         }.joinToString("")
     }.joinToString("") }
    ${StrUtil.upperFirst(rootEntity.enName)}DO ${StrUtil.lowerFirst(rootEntity.enName)}EntityToDO(${StrUtil.upperFirst(rootEntity.enName)}AggregateRootEntity entity);
    /**
     * DO转实体 ${rootEntity.name}
     */${rootValueObjects.map {"""
    @Mapping(target = "${StrUtil.lowerFirst(it.enName)}", source = ".", qualifiedByName = "to${StrUtil.upperFirst(it.enName)}")"""
            }.joinToString("")
     }
    ${StrUtil.upperFirst(rootEntity.enName)}AggregateRootEntity ${StrUtil.lowerFirst(rootEntity.enName)}DoToEntity(${StrUtil.upperFirst(rootEntity.enName)}DO ${StrUtil.lowerFirst(rootEntity.enName)}DO);

    ${rootValueObjects.map{vo ->
        """
    @Named("to${StrUtil.upperFirst(vo.enName)}")
    default ${StrUtil.upperFirst(vo.enName)} to${StrUtil.upperFirst(vo.enName)}(${StrUtil.upperFirst(rootEntity.enName)}DO ${StrUtil.lowerFirst(rootEntity.enName)}DO) {
        if (${StrUtil.lowerFirst(rootEntity.enName)}DO == null) {
            return null;
        }
        //todo 其他判断逻辑
 
        return new ${StrUtil.upperFirst(vo.enName)}(${vo.fields.map{"${StrUtil.lowerFirst(rootEntity.enName)}DO.get${StrUtil.upperFirst(it.enName)}()"}.joinToString(",")});
    }"""
        
     }.joinToString("\n") }

    ${rootEntity.entities.map { entity ->
                val entityValueObjects = valueObjects.filter { entity.fields.map { f -> f.enName }.contains(it.enName) }
                    .filter { it.fields.size > 1 } 
                """
    /**
     * 实体转DO ${entity.name}
     */${entityValueObjects.map{vo -> vo.fields.map { field ->"""
    @Mapping(target = "${StrUtil.lowerFirst(field.enName)}", source = "${StrUtil.lowerFirst(vo.enName)}", qualifiedByName = "to${StrUtil.upperFirst(field.enName)}")""".trimIndent()
                    }.joinToString("")
                }.joinToString("") }
    ${StrUtil.upperFirst(entity.enName)}DO ${StrUtil.lowerFirst(entity.enName)}EntityToDO(${StrUtil.upperFirst(entity.enName)}Entity entity);
    /**
     * DO转实体 ${entity.name}
     */${entityValueObjects.map { """
    @Mapping(target = "${StrUtil.lowerFirst(it.enName)}", source = ".", qualifiedByName = "to${StrUtil.upperFirst(it.enName)}")"""
                }.joinToString("")
                }
    ${StrUtil.upperFirst(entity.enName)}Entity ${StrUtil.lowerFirst(entity.enName)}DoToEntity(${StrUtil.upperFirst(entity.enName)}DO ${StrUtil.lowerFirst(entity.enName)}DO);

    ${entityValueObjects.map{vo ->
                    """
    @Named("to${StrUtil.upperFirst(vo.enName)}")
    default ${StrUtil.upperFirst(vo.enName)} to${StrUtil.upperFirst(vo.enName)}(${StrUtil.upperFirst(entity.enName)}DO ${StrUtil.lowerFirst(entity.enName)}DO) {
        if (${StrUtil.lowerFirst(entity.enName)}DO == null) {
            return null;
        }
        //todo 其他判断逻辑
 
        return new ${StrUtil.upperFirst(vo.enName)}(${vo.fields.map{"${StrUtil.lowerFirst(entity.enName)}DO.get${StrUtil.upperFirst(it.enName)}()"}.joinToString(",")});
    }
        """
                }.joinToString("\n\n") }                
                    
                """
    
    
    }.joinToString("\n\n") }
}
                """

            createFile(adapterRootPath.appendNextPath("${StrUtil.upperFirst(rootEntity.enName)}Converter.java"), converterContent)
        }

        private fun generateDO(
            aggregateRoot: AggregateRootEntity,
            doName:String,
            doEnName:String,
            adapterRootPath: String,
            adapterJavaPath: String,
            fields: List<Field>
        ) {
            val multiTenant = aggregateRoot.multiTenant

            val doContent = """
package $adapterJavaPath;

import com.baomidou.mybatisplus.annotation.TableName;
import ${if(multiTenant)"com.zz.core.tenant.mp.TenantDO" else "com.zz.core.mp.base.BaseDO"};
import lombok.Data;

 /**
  * $doName DO
  * @author ${aggregateRoot.creator}<p>
  * ================================<p>
  * Date: ${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))}<p>
  * Time: ${LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"))}<p>
  * ================================
  */
@Data
@TableName("tb_${StrUtil.toUnderlineCase(doEnName)}")
public class ${StrUtil.upperFirst(doEnName)}DO extends ${if(multiTenant)"TenantDO" else "BaseDO"} {
    ${fields.map {
                """
    /**
     * ${it.name}
     */
    private ${StrUtil.upperFirst(it.type)} ${it.enName};
        """}.joinToString("")}
}"""

            createFile(adapterRootPath.appendNextPath("${StrUtil.upperFirst(doEnName)}DO.java"), doContent)

            val mapperContent = """
package $adapterJavaPath;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
  * $doName Mapper
  * @author ${aggregateRoot.creator}<p>
  * ================================<p>
  * Date: ${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))}<p>
  * Time: ${LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"))}<p>
  * ================================
  */
@Mapper
public interface ${StrUtil.upperFirst(doEnName)}Mapper extends BaseMapper<${StrUtil.upperFirst(doEnName)}DO> {
}
                """
            createFile(adapterRootPath.appendNextPath("${StrUtil.upperFirst(doEnName)}Mapper.java"), mapperContent)
        }

        private fun generateAdapterPomXml(
            aggregateRoot: AggregateRootEntity,
            path: String
        ) {
            val xmlContent =
"""<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>${aggregateRoot.comPrefix}</groupId>
        <artifactId>${aggregateRoot.moduleName.lowercase()}bc-southbound</artifactId>
        <version>${aggregateRoot.version}</version>
    </parent>

    <artifactId>${aggregateRoot.moduleName.lowercase()}bc-southbound-adapter</artifactId>

    <dependencies>
        <dependency>
            <groupId>${aggregateRoot.comPrefix}</groupId>
            <artifactId>${aggregateRoot.moduleName.lowercase()}bc-northbound-local</artifactId>
            <version>${'$'}{zz.project.version}</version>
        </dependency>
        <dependency>
            <groupId>com.zz</groupId>
            <artifactId>zz-starter-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zz</groupId>
            <artifactId>zz-starter-tenant</artifactId>
        </dependency>
    </dependencies>
</project>
                """
            createFile(path.appendNextPath("pom.xml"), xmlContent)
        }

        private fun generateSouthboundPomXml(
            aggregateRoot: AggregateRootEntity,
            path: String
        ) {
            val xmlContent =
"""<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>${aggregateRoot.comPrefix}</groupId>
    <artifactId>${aggregateRoot.moduleName.lowercase()}bc</artifactId>
    <version>${aggregateRoot.version}</version>
  </parent>
  <artifactId>${aggregateRoot.moduleName.lowercase()}bc-southbound</artifactId>
  <packaging>pom</packaging>
  <modules>
    <module>${aggregateRoot.moduleName.lowercase()}bc-southbound-adapter</module>
  </modules>
</project>
           
            """
            createFile(path.appendNextPath("pom.xml"), xmlContent)
        }


    }

}